﻿using DocumentGeneratorPlugins;
using DocumentorLog;
using OpenXmlDocumentGenerator;
using OpenXMLDocumentGenerator.Helpers;
using OpenXMLDocumentGenerator.Model;
using SPDocumentor.SPHelpers;
using SPDocumentor.SPModel;
using SPDocumentor.SPModel.SPServiceApplications;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SPDocumentor
{
    public class SharePointFarmDocumentor : ISystemDocumentor
    {
        public event DocumentGeneratorEventHandler DocumentGeneratorEvent;
        private string[] propertyWebAppsToExclude = new string[] { "SiteCols", "JobHistories" };
        private string[] propertySiteColToExclude = new string[] { "Sites", "FeatureDefinition" };
        private string[] propertySitesToExclude = new string[] { "FeatureDefinition","Features", "Lists" };
        public void GenerateDocument(IDocumentOptions options)
        {
            OnDocumentGeneratorEvent("Get Farm information", StateStep.Start);
            Log.Info("Start Farm Documentor");
            FarmLoader.FarmEvent += FarmLoader_FarmEvent;
            Farm frm = FarmLoader.Current.LoadFarm();
            string strPathzip = options.DocumentPath.Replace("docx", "documentor");
            ZipSerializer.Serialize(frm, strPathzip);
            OnDocumentGeneratorEvent("Start Creating Document", StateStep.Progress);
            WordDocumentHelpers wdh = new WordDocumentHelpers();
            wdh.CreatePackage(options);
            wdh.AddTitleParagraph(options.DocumentPropertiesTitle);
            FarmOverview(frm, wdh);
            ServersInformations(frm.Servers, wdh);
            AlternateAccessMappingInformation(frm.AlternateUrlCollection, wdh);
            FeatureDefinitionInformation(frm.FeatureDefinitions, wdh);
            FeatureInformation(frm.Features, wdh);
            ServiceApplicationProxyGroupInformation(frm.ServiceApplicationProxyGroups, wdh);
            SolutionsInformation(frm.Solutions, wdh);
            TimerJobInformation(frm.Servers, wdh);
            WebAppsInformation(frm.WebApps, wdh);
            ServiceApplicationInformation(frm.Servers, wdh);
            wdh.Dispose();
            OnDocumentGeneratorEvent("Finish Creating Document", StateStep.Finish);
            
        }

        void FarmLoader_FarmEvent(object sender, SharePointDocumentor.SPModel.FarmEventArgs fea)
        {
           OnDocumentGeneratorEvent(fea.Message);
        }

        protected virtual void OnDocumentGeneratorEvent(String Message, StateStep Step)
        {
            if (DocumentGeneratorEvent != null)
            {
                DocumentGeneratorEventArgs dgea = new DocumentGeneratorEventArgs();
                dgea.Message = Message;
                dgea.State = Step;
                DocumentGeneratorEvent(this, dgea);
            }
        }

        private void ServiceApplicationInformation(Server[] servers, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Services Application");
            foreach (Server srv in servers)
            {
                foreach (Service svc in srv.Services)
                {
                    foreach (ServiceApplication sa in svc.ServiceApplications)
                    {
                        AddServiceApplicationParagraph(sa, wdh);
                    }
                }
            }
        }

        private void OnDocumentGeneratorEvent(string Message)
        {
            OnDocumentGeneratorEvent(Message, StateStep.Progress);
        }

        private void AddServiceApplicationParagraph(ServiceApplication sa, WordDocumentHelpers wdh)
        {
            wdh.AddHeader2Paragraph(sa.DisplayName);
            wdh.AddNormalParagraph();
            PropertyModel[] pms = RenderTypeHelper.GetPropertyFromObject(sa, null);
            foreach (PropertyModel pm in pms.Where(p => p.Child == null))
                RenderPropertyValue(pm, 4, wdh);
            foreach (PropertyModel pm in pms.Where(p => p.Child != null))
                RenderPropertyValue(pm, 3, wdh);

        }
        private static void RenderPropertyValue(PropertyModel pm, int Level, WordDocumentHelpers wdh)
        {
            if (string.IsNullOrWhiteSpace(pm.Value) && pm.Values == null && pm.Child == null)
                return;

            switch (Level)
            {
                case 1:
                    wdh.AddHeader1Paragraph(pm.Name);
                    wdh.AddNormalParagraph();
                    break;
                case 2:
                    wdh.AddHeader2Paragraph(pm.Name);
                    wdh.AddNormalParagraph();
                    break;
                case 3:
                    wdh.AddHeader3Paragraph(pm.Name);
                    wdh.AddNormalParagraph();
                    break;
                default:
                    wdh.AddTextBold(pm.Name + ": "); wdh.AddTab();
                    break;
            }
            Level++;
            if (pm.Value != null)
            {
                wdh.AddText(pm.Value);
                wdh.AddBreak();
            }
            if (pm.Values != null)
                foreach (string s in pm.Values)
                {
                    wdh.AddText(pm.Value);
                    wdh.AddBreak();
                }
            if (pm.Child != null)
                foreach (PropertyModel pmChild in pm.Child)
                    RenderPropertyValue(pmChild, Level, wdh);
        }


        private void WebAppsInformation(WebApplication[] webApplication, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("WebApplications");
            foreach (WebApplication wa in webApplication)
            {
                wdh.AddHeader2Paragraph(wa.DisplayName);
                FillWebAppInformation(wa, wdh);
                foreach (SiteCollection sc in wa.SiteCols)
                {

                    FillSiteCollectionInformation(sc, wdh);
                    foreach (Site s in sc.Sites)
                    {
                        FillSiteInformation(s, wdh);
                    }
                }

            }

        }
        

        private void FillSiteInformation(Site s, WordDocumentHelpers wdh)
        {
            wdh.AddNormalParagraph();

            wdh.AddHeader4Paragraph(s.SiteURL + " - Site Web");

            PropertyModel[] pms = RenderTypeHelper.GetPropertyFromObject(s, propertySitesToExclude);

            RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);

            wdh.AddHeader5Paragraph(s.SiteURL + " - Site Web List");
            foreach (SharePointDocumentor.SPModel.SPDList l in s.Lists)
            {
                wdh.AddNormalParagraph();
                wdh.AddTextBold(l.Title);
            
                pms = RenderTypeHelper.GetPropertyFromObject(l, new string[]{"Title"});

                RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);
            }
            wdh.AddHeader5Paragraph(s.SiteURL + " - Site Web Workflow Template");
            foreach (SharePointDocumentor.SPModel.WorkflowTemplate f in s.WorkflowTemplates)
            {
                wdh.AddNormalParagraph();
                wdh.AddTextBold(f.Name);

                pms = RenderTypeHelper.GetPropertyFromObject(f, new string[] { "Name" });

                RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);
            }
            wdh.AddHeader5Paragraph(s.SiteURL + " - Site Web Features");
            foreach (Feature f in s.Features)
            {
                wdh.AddNormalParagraph();
                wdh.AddTextBold(f.FeatureName);

                pms = RenderTypeHelper.GetPropertyFromObject(f, new string[] { "FeatureName" });

                RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);
            }

        }

        private void FillSiteCollectionInformation(SiteCollection sc, WordDocumentHelpers wdh)
        {
            wdh.AddHeader3Paragraph(sc.SiteCollectionURL + " - Site Collection");
            PropertyModel[] pms = RenderTypeHelper.GetPropertyFromObject(sc, propertySiteColToExclude);

            RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);
        }

        private void FillWebAppInformation(WebApplication wa, WordDocumentHelpers wdh)
        {
            PropertyModel[] pms = RenderTypeHelper.GetPropertyFromObject(wa, propertyWebAppsToExclude);

            RenderTypeHelper.RenderPropertyModelForTable(pms, wdh);
        }

        private void TimerJobInformation(Server[] servers, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Timer Jobs");
            List<List<string>> lstFeatureDefinition = new List<List<string>>();
            List<string> lstFeatureDefinitionHead = new List<string>();
            lstFeatureDefinitionHead.Add("Nom");
            lstFeatureDefinitionHead.Add("Description");
            lstFeatureDefinitionHead.Add("Serveur");
            lstFeatureDefinitionHead.Add("Service");

            lstFeatureDefinition.Add(lstFeatureDefinitionHead);
            foreach (Server srv in servers)
            {
                foreach (Service svc in srv.Services)
                {
                    foreach (JobDefinition jd in svc.JobDefinitions)
                    {
                        List<string> lstFeatureDefinitionProp = new List<string>();
                        lstFeatureDefinitionProp.Add(jd.DisplayName);
                        lstFeatureDefinitionProp.Add(jd.Description);
                        lstFeatureDefinitionProp.Add(jd.ServerName);
                        lstFeatureDefinitionProp.Add(jd.ServiceName);
                        lstFeatureDefinition.Add(lstFeatureDefinitionProp);
                    }

                }
            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstFeatureDefinition);
        }

        private void SolutionsInformation(Solutions[] solutions, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Solutions");
            List<List<string>> lstSolution = new List<List<string>>();
            List<string> lstSolutionHead = new List<string>();
            lstSolutionHead.Add("Nom");
            lstSolutionHead.Add("Status");
            lstSolutionHead.Add("Cas Policy");
            lstSolutionHead.Add("Global Assembly");
            lstSolutionHead.Add("WebApplication");
            lstSolution.Add(lstSolutionHead);
            foreach (Solutions sol in solutions)
            {
                List<string> lstSolutionProp = new List<string>();

                lstSolutionProp.Add(sol.DisplayName);
                lstSolutionProp.Add(sol.DeploymentState);
                StringBuilder sbwa = new StringBuilder();

                if (sol.ContainsWebApplicationResource)
                    foreach (WebApplication wa in sol.DeployedWebApplications)
                    {
                        sbwa.Append(wa.DisplayName + "\n");
                    }
                lstSolutionProp.Add(sol.ContainsCasPolicy.ToString());
                lstSolutionProp.Add(sol.ContainsGlobalAssembly.ToString());
                lstSolutionProp.Add(sbwa.ToString());
                lstSolution.Add(lstSolutionProp);
            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstSolution);
        }

        private void ServiceApplicationProxyGroupInformation(ApplicationProxyGroup[] applicationProxyGroup, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Application Proxy group");
            foreach (ApplicationProxyGroup fd in applicationProxyGroup)
            {
                wdh.AddHeader2Paragraph(fd.DisplayName);
                List<List<string>> lstApplicationProxy = new List<List<string>>();
                List<string> lstApplicationProxyHead = new List<string>();
                lstApplicationProxyHead.Add("Nom");
                lstApplicationProxyHead.Add("ID");
                lstApplicationProxy.Add(lstApplicationProxyHead);
                foreach (ApplicationProxy ap in fd.Proxies)
                {
                    List<string> lstApplicationProxyProp = new List<string>();
                    lstApplicationProxyProp.Add(ap.DisplayName);
                    lstApplicationProxyProp.Add(ap.ID.ToString());
                    lstApplicationProxy.Add(lstApplicationProxyProp);
                }
                wdh.AddTable(TableStyleEnum.StyleGrid1, lstApplicationProxy);
            }

        }

        private void FeatureInformation(Feature[] feature, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Feature de la ferme");
            List<List<string>> lstFeature = new List<List<string>>();
            List<string> lstFeatureHead = new List<string>();
            lstFeatureHead.Add("Nom");
            lstFeatureHead.Add("ID");
            lstFeatureHead.Add("Version");
            lstFeature.Add(lstFeatureHead);
            foreach (Feature f in feature)
            {

                List<string> lstFeatureProp = new List<string>();
                lstFeatureProp.Add(f.FeatureDefinition.DisplayName);
                lstFeatureProp.Add(f.FeatureID.ToString());
                lstFeatureProp.Add(f.Version.ToString());
                lstFeature.Add(lstFeatureProp);
            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstFeature);
        }

        private void FeatureDefinitionInformation(FeatureDefinition[] featureDefinition, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Feature Definition");
            List<List<string>> lstFeatureDefinition = new List<List<string>>();
            List<string> lstFeatureDefinitionHead = new List<string>();
            lstFeatureDefinitionHead.Add("Nom");
            lstFeatureDefinitionHead.Add("Masqué");
            lstFeatureDefinitionHead.Add("Has Receiver");
            lstFeatureDefinitionHead.Add("Scope");

            lstFeatureDefinition.Add(lstFeatureDefinitionHead);
            foreach (FeatureDefinition fd in featureDefinition)
            {
                List<string> lstFeatureDefinitionProp = new List<string>();
                lstFeatureDefinitionProp.Add(fd.DisplayName + "\n" + fd.FeatureID.ToString() + "\n" + fd.Version);
                lstFeatureDefinitionProp.Add(fd.Hidden.ToString());
                bool bHasReceiver = !string.IsNullOrEmpty(fd.ReceiverAssembly) && !string.IsNullOrEmpty(fd.ReceiverClass);
                lstFeatureDefinitionProp.Add(bHasReceiver.ToString());
                lstFeatureDefinitionProp.Add(fd.Scope);
                lstFeatureDefinition.Add(lstFeatureDefinitionProp);
            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstFeatureDefinition);
        }

        private void AlternateAccessMappingInformation(AlternateUrl[] alternateUrl, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Alternate Access Mapping");
            List<List<string>> lstAlternateUrl = new List<List<string>>();
            List<string> lstAlternateUrlHead = new List<string>();
            lstAlternateUrlHead.Add("Nom");
            lstAlternateUrlHead.Add("Defaut");
            lstAlternateUrlHead.Add("Internet");
            lstAlternateUrlHead.Add("Intranet");
            lstAlternateUrlHead.Add("Extranet");
            lstAlternateUrlHead.Add("Custom");
            lstAlternateUrl.Add(lstAlternateUrlHead);
            foreach (AlternateUrl au in alternateUrl)
            {
                List<string> lstAlternateUrlProp = new List<string>();
                lstAlternateUrlProp.Add(au.DisplayName);
                lstAlternateUrlProp.Add(au.DefaultZoneURL);
                lstAlternateUrlProp.Add(au.InternetZoneURL);
                lstAlternateUrlProp.Add(au.IntranetZoneURL);
                lstAlternateUrlProp.Add(au.ExtranetZoneURL);
                lstAlternateUrlProp.Add(au.CustomZoneURL);
                lstAlternateUrl.Add(lstAlternateUrlProp);
            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstAlternateUrl);
        }

        private static void ServersInformations(Server[] srvs, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Les Serveurs de la ferme");
            List<List<string>> lstServer = new List<List<string>>();
            List<string> lstServerHead = new List<string>();
            lstServerHead.Add("Nom du server");
            lstServerHead.Add("Rôle");
            lstServerHead.Add("Mémoire");
            lstServerHead.Add("Lecteurs");
            lstServerHead.Add("Patches");
            lstServerHead.Add("Services");
            lstServer.Add(lstServerHead);
            foreach (Server srv in srvs)
            {
                List<string> lstServerProp = new List<string>();
                lstServerProp.Add(srv.ServerName + "\n" + srv.OperatingSystem);
                lstServerProp.Add(srv.Role);
                lstServerProp.Add(Math.Round(srv.Memory).ToString() + "Go");
                StringBuilder strDrives = new StringBuilder();
                foreach (Drive drv in srv.Drives)
                {
                    string strTotalSpace = "";
                    if (!string.IsNullOrEmpty(drv.TotalSpace))
                        if (decimal.Parse(drv.TotalSpace) / 1048576 > 1000)
                        {
                            strTotalSpace = Math.Round(decimal.Parse(drv.TotalSpace) / 1073741824).ToString() + " Go";
                        }
                        else
                        {
                            strTotalSpace = Math.Round(decimal.Parse(drv.TotalSpace) / 1048576).ToString() + " Mo";
                        }

                    strDrives.Append(drv.Letter + strTotalSpace + "\n");
                }
                lstServerProp.Add(strDrives.ToString());
                StringBuilder strPatch = new StringBuilder();
                foreach (ServerPatche fix in srv.Patches)
                {
                    strPatch.Append(fix.Status + "\n-------");
                    if (!string.IsNullOrEmpty(fix.Caption)) strPatch.Append(fix.Caption + "\n");
                    if (!string.IsNullOrEmpty(fix.Description)) strPatch.Append(fix.Description + "\n");
                    if (!string.IsNullOrEmpty(fix.HotfixID)) strPatch.Append(fix.HotfixID + "\n");
                    if (!string.IsNullOrEmpty(fix.InstallDate)) strPatch.Append(fix.InstallDate + "\n");
                    if (!string.IsNullOrEmpty(fix.InstalledBy)) strPatch.Append(fix.InstalledBy + "\n");
                    if (!string.IsNullOrEmpty(fix.InstalledOn)) strPatch.Append(fix.InstalledOn + "\n");
                    if (!string.IsNullOrEmpty(fix.ServicePackInEffect)) strPatch.Append(fix.ServicePackInEffect + "\n");
                    strPatch.Append(fix.Status + "-------\n");
                }
                lstServerProp.Add(strPatch.ToString());


                StringBuilder strSvc = new StringBuilder();
                foreach (Service svc in srv.Services)
                {
                    strSvc.Append(svc.DisplayName + "\n");
                }
                lstServerProp.Add(strSvc.ToString());

                lstServer.Add(lstServerProp);

            }
            wdh.AddTable(TableStyleEnum.StyleGrid1, lstServer);
        }

        private static void FarmOverview(Farm frm, WordDocumentHelpers wdh)
        {
            wdh.AddHeader1Paragraph("Apercu de la ferme");
            wdh.AddNormalParagraph();
            wdh.AddTextBold("Version de SharePoint: ");
            wdh.AddTab();
            wdh.AddText(frm.SharePointVersion);
            wdh.AddNormalParagraph();
            wdh.AddTextBold("Edition: ");
            wdh.AddBreak();
            foreach (string str in frm.SharePointEdition)
            {
                wdh.AddTab();
                wdh.AddText(str);
                wdh.AddBreak();
            }
            wdh.AddNormalParagraph();
            wdh.AddTextBold("Base de données de Configuration: ");
            wdh.AddText(frm.SharePointConfigDB);

            wdh.AddNormalParagraph();
            wdh.AddTextBold("Nombre de server: ");
            wdh.AddText(frm.Servers.Count().ToString());

        }


        public string PluginName
        {
            get { return "SharePoint Farm Documentation"; }
        }


        public IDocumentOptions CreateDocumentOption()
        {
            return new SPDocumentOption();
        }


        public System.Windows.Controls.UserControl GetUserInterface()
        {
            return (System.Windows.Controls.UserControl)(new SharePointDocumentor.UI.SharePointDocumentorUI(this));
        }

        public Logger Log
        {
            get { return Logger.Current; }
        }
    }
}
